import { Injectable } from '@angular/core';
import { Observable, Subject } from 'rxjs';

export enum ThemeType {
  aliyun = 'aliyun',
  compact = 'compact',
  dark = 'dark',
  default = 'default'
}

@Injectable({
  providedIn: 'root'
})
export class ThemeService {
  private _currentTheme!: ThemeType;
  private theme$ = new Subject<ThemeType>();

  public onThemeTypeChanged = this.theme$.asObservable();

  constructor() {}

  private reverseTheme(theme: string | null): ThemeType {
    var rslt = ThemeType[theme as keyof typeof ThemeType];
    if (rslt === undefined) {
      rslt = ThemeType.default;
    }
    return rslt;
  }

  private removeUnusedTheme(theme: ThemeType): void {
    document.documentElement.classList.remove(theme);
    const removedThemeStyle = document.getElementById(theme);
    if (removedThemeStyle) {
      document.head.removeChild(removedThemeStyle);
    }
  }

  private loadCss(href: string, id: string): Promise<Event> {
    return new Promise((resolve, reject) => {
      const style = document.createElement('link');
      style.rel = 'stylesheet';
      style.type = 'text/css';
      style.href = href;
      style.id = id;
      style.onload = resolve;
      style.onerror = reject;
      document.head.append(style);
    });
  }

  private loadTheme(theme: ThemeType, firstLoad = false): Promise<Event> {
    if (firstLoad) {
      document.documentElement.classList.add(theme);
    }
    return new Promise<Event>((resolve, reject) => {
      this.loadCss(`${theme}.css`, theme).then(
        e => {
          if (!firstLoad) {
            document.documentElement.classList.add(theme);
          }
          if (this._currentTheme) {
            this.removeUnusedTheme(this._currentTheme);
          }
          this._currentTheme = theme;
          resolve(e);
        },
        e => reject(e)
      );
    });
  }

  private checkTheme(themeStr: string): Promise<ThemeType> {
    return new Promise<ThemeType>((resolve, reject) => {
      const theme = this.reverseTheme(themeStr);
      if (theme === this._currentTheme) {
        return reject(new Error('加载相同的主题'));
      }
      return resolve(theme);
    });
  }

  public changeTheme(themeStr: string): Promise<Event> {
    return this.checkTheme(themeStr).then(theme => this.loadTheme(theme));
  }

  public initTheme(themeStr: string): Promise<Event> {
    return this.checkTheme(themeStr).then(theme => this.loadTheme(theme, true));
  }

  publish(themeStr: string): void {
    const themeTmp = this.reverseTheme(themeStr);
    if (themeTmp === undefined) {
      return;
    }
    this.theme$.next(themeTmp);
  }

  get currentTheme(): ThemeType {
    return this._currentTheme;
  }
}
